package com.drakulo.hi.gfx;

import java.awt.Point;
import java.util.ArrayList;
import java.util.List;

import org.newdawn.slick.Graphics;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.UnicodeFont;

import com.dk.hi.core.Block;
import com.dk.hi.core.BlockMap;
import com.dk.hi.core.Direction;
import com.dk.hi.core.Settings;
import com.dk.hi.gui.MapActor;
import com.drakulo.hi.gfx.panel.Panel;

/**
 * <p>
 * Cette classe rassemble les coordonnées utilisées pour l'affichage de la carte
 * courante. Elle est entièrement statique pour être accédée depuis n'importe
 * quel endroit.
 * </p>
 */
@Deprecated
public class GameViewPort {

	/**
	 * Carte courante
	 */
	private static BlockMap map;

	/**
	 * Héros
	 */
	private static MapActor player;

	/**
	 * Point de destination du joueur
	 */
	private static Point destination;

	/**
	 * La boite de dialogues
	 */
	private static TextBox textBox;

	/**
	 * Panneau central. Sert à diverses choses...
	 */
	private static Panel panel;

	/**
	 * Ajoute un MapActor à la carte courante
	 * 
	 * @param actor
	 *            - l'acteur initialisé à ajouter
	 */
	public static void addActor(MapActor actor) {
		// map.addActor(actor);
	}

	private static void debug() throws SlickException {
		int x = 100;
		int x2 = 180;
		int y = 2;
		UnicodeFont font = FontFactory.getVisitor10_white();

		if (destination != null) {
			font.drawString(x, y, "destX");
			font.drawString(x2, y, ": " + destination.x);
			y += 10;
			font.drawString(x, y, "destY");
			font.drawString(x2, y, ": " + destination.y);
		}

		int playerScreenX = Settings.WINDOW_WIDTH / 2;
		int playerScreenY = Settings.WINDOW_HEIGHT / 2;
		int playerMapX = getScreenX(playerScreenX);
		int playerMapY = getScreenY(playerScreenY);

		y += 10;
		font.drawString(x, y, "playerX");
		font.drawString(x2, y, ": " + playerMapX);
		y += 10;
		font.drawString(x, y, "playerY");
		font.drawString(x2, y, ": " + playerMapY);
	}

	/**
	 * @return la liste de toutes les entités de la carte :
	 *         <ul>
	 *         <li>Les cases dont le franchissement est spécial</li>
	 *         <li>Les acteurs</li>
	 *         </ul>
	 * @throws HIException
	 */
	private static List<Block> getAllEntities() {
		List<Block> entities = new ArrayList<Block>();

		// entities.addAll(map.getMapEntities());
		// entities.addAll(map.getActors());

		return entities;
	}

	/**
	 * @return le point de destination du héros
	 */
	public static Point getDestination() {
		return destination;
	}

	/**
	 * @return la carte courante
	 */
	public static BlockMap getMap() {
		return map;
	}

	/**
	 * Calcule la coordonnée en X par rapport à une coordonnée à l'écran
	 * 
	 * @param x
	 *            - la coordonnée à l'écran
	 * @return la coordonnée sur la carte
	 */
	public static int getMapX(int x) {
		return x - map.getX();
	}

	/**
	 * Calcule la coordonnée en X par rapport à une coordonnée à l'écran
	 * 
	 * @param y
	 *            - la coordonnée à l'écran
	 * @return la coordonnée sur la carte
	 */
	public static int getMapY(int y) {
		return y - map.getY();
	}

	/**
	 * @return le panneau
	 */
	public static Panel getPanel() {
		return panel;
	}

	/**
	 * @return la direction actuele du joueur
	 */
	public static Direction getPlayerDirection() {
		return player.getCurrentDirection();
	}

	/**
	 * @return la position en X du personnage sur la carte
	 */
	public static int getPlayerMapX() {
		return getMapX(player.getX());
	}

	/**
	 * @return la position en Y du joueur sur la carte
	 */
	public static int getPlayerMapY() {
		return getMapY(player.getY());
	}

	/**
	 * Renvoie la coordonnée par rapport à l'écran à partir de la coordonnée par
	 * rapport à la carte
	 * 
	 * @param x
	 *            - la coordonnée en X
	 * @return la coordonnée transformée par rapport à l'écran
	 */
	public static int getScreenX(int x) {
		return x + map.getX();
	}

	/**
	 * Renvoie la coordonnée par rapport à l'écran à partir de la coordonnée par
	 * rapport à la carte
	 * 
	 * @param y
	 *            - la coordonnée en Y
	 * @return la coordonnée transformée par rapport à l'écran
	 */
	public static int getScreenY(int y) {
		return y + map.getY();
	}

	/**
	 * @return la boite de texte actuelle
	 */
	public static TextBox getTextBox() {
		return textBox;
	}

	/**
	 * Gère les cycles de jeu, avec le déplacement des acteurs, déplacement du
	 * joueur et actions de la part du joueur.
	 * 
	 * @param input
	 *            - Entrées utilisateur
	 */
	private static void handleGameLoop(Input input) {
		boolean leftClick = input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON);
		if (leftClick) {
			destination = new Point(getMapX(input.getMouseX()), getMapY(input.getMouseY()));
		}

		int playerMapX = getMapX(player.getX());
		int playerMapY = getMapY(player.getY());
		Point playerPosition = new Point(playerMapX, playerMapY);

		if (destination != null) {
			player.startMoving();
			if (playerPosition.equals(destination)) {
				// On est arrivé à destination ou on n'en avait pas. Dans les
				// deux cas, on ne bouge plus
				destination = null;
				player.stopMoving();
			} else {
				// On n'est pas arrivé à destination; il faut onc déplacer le
				// viewport
				int dx = 0;
				int dy = 0;
				boolean moved = false;
				if (destination.x > playerMapX + 32) {
					// Il faut déplacer la carte à gauche pour avoir
					// l'impression d'aller à droite
					dx = -1;
					player.setDirection(Direction.RIGHT);
					moved = true;
				} else if (destination.x < playerMapX) {
					// Il faut déplacer la carte à droite pour avoir
					// l'impression d'aller à gauche
					dx = 1;
					moved = true;
					player.setDirection(Direction.LEFT);
				}
				if (destination.y > playerMapY + 32) {
					// Il faut déplacer la carte à gauche pour avoir
					// l'impression d'aller à droite
					dy = -1;
					moved = true;
					player.setDirection(Direction.DOWN);
				} else if (destination.y < playerMapY) {
					// Il faut déplacer la carte à droite pour avoir
					// l'impression d'aller à gauche
					dy = 1;
					moved = true;
					player.setDirection(Direction.UP);
				}
				if (!moved) {
					destination = null;
					player.stopMoving();
				}
				map.moveOrigin(dx, dy);
				if (isInCollision(player)) {
					map.moveOrigin(-dx, -dy);
					destination = null;
					player.stopMoving();
				}
			}
		}

		// for (MapActor actor : map.getActors()) {
		// actor.update(input);
		// }
	}

	/**
	 * @param actor
	 *            - l'acteur à tester
	 * @return vrai si le héros regarde dans la direction de l'acteur
	 */
	public static boolean heroIsFacing(MapActor actor) {
		int playerX = GameViewPort.getMapX(player.getX());
		int playerY = GameViewPort.getMapY(player.getY());
		int actorX = actor.getX();
		int actorY = actor.getY();

		switch (player.getCurrentDirection()) {
		case RIGHT:
			if (actorX < playerX) {
				return true;
			}
		case LEFT:
			if (actorX > playerX) {
				return true;
			}
		case UP:
			if (actorY < playerY) {
				return true;
			}
		case DOWN:
			if (actorY > playerY) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Initialise la carte
	 */
	public static void initialize() {
		map = null;
		player = null;
		destination = null;
	}

	/**
	 * Teste la collision entre le Block passé en paramètre et toutes les
	 * entités de la carte (décors et acteurs)
	 * 
	 * @param block
	 *            - le bloc dont on veut tester la collision
	 * @return Vrai si le bloc est en collision, ou faux si ce n'est pas le cas
	 */
	public static boolean isInCollision(Block block) {
		for (Block entity : GameViewPort.getAllEntities()) {
			if (block.collide(entity)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Teste si un block unique est en collision avec le héros
	 * 
	 * @param block
	 *            - Block à tester
	 * @return vrai si le block est en collision avec le héros
	 */
	public static boolean isInCollisionWithPlayer(Block block) {
		return block.collide(player);
	}

	/**
	 * Charge la carte à partir du chemin de son fichier de configuration
	 * 
	 * @param mapName
	 *            - le chemin du fichier
	 * @throws SlickException
	 */
	public static void loadMap(String mapName) throws SlickException {
		map = new BlockMap(mapName);
	}

	/**
	 * Déplace la carte
	 * 
	 * @param x
	 *            - composante en X
	 * @param y
	 *            - composante en Y
	 */
	public static void moveMap(float x, float y) {
		map.moveOrigin(x, y);
	}

	/**
	 * Génère le rendu de tout ce qui concerne la carte :
	 * <ul>
	 * <li>Le décors</li>
	 * <li>Le héros</li>
	 * <li>Les autres acteurs</li>
	 * </ul>
	 * 
	 * @param g
	 *            - le conteneur graphique
	 * @throws SlickException
	 * @throws HIException
	 */
	public static void render(Graphics g) throws SlickException {
		// Rendu de la carte
		map.render(g);

		if (destination != null) {
			g.drawOval(getScreenX(destination.x), getScreenY(destination.y), 5, 5);
		}

		// Gestion des "dessus-dessous" afin d'aficher correctement le sprite du
		// héros s'il est devant ou derrière un acteur
		List<MapActor> actorsToRenderBefore = new ArrayList<MapActor>();
		List<MapActor> actorsToRenderAfter = new ArrayList<MapActor>();

		int playerY = getPlayerMapY();
		// for (MapActor actor : map.getActors()) {
		// if (playerY < actor.getY()) {
		// actorsToRenderAfter.add(actor);
		// } else {
		// actorsToRenderBefore.add(actor);
		// }
		// }

		// Rendus des acteurs derrière le héros
		// for (MapActor actor : actorsToRenderBefore) {
		// actor.renderOn(g, false);
		// }
		//
		// // Rendu du héros
		// player.renderOn(g, true);
		//
		// // Rendu des acteurs devant le héros
		// for (MapActor actor : actorsToRenderAfter) {
		// actor.renderOn(g, false);
		// }

		// characterZone.render(g);
		if (panel != null) {
			panel.render(g);
		}

		if (textBox != null) {
			textBox.render(g);
		}

		debug();
	}

	/**
	 * Initialise le point de destination du héros
	 * 
	 * @param destination
	 *            - la destination
	 */
	public static void setDestination(Point destination) {
		player.stopMoving();
		GameViewPort.destination = destination;
	}

	/**
	 * Initialise la carte
	 * 
	 * @param newMap
	 *            - la nouvelle carte
	 * @throws HIException
	 */
	public static void setMap(BlockMap newMap) {
		map = newMap;
	}

	/**
	 * Initialise le panneau
	 * 
	 * @param panel
	 *            - le panneau
	 */
	public static void setPanel(Panel panel) {
		GameViewPort.panel = panel;
	}

	/**
	 * Initialise le héros
	 * 
	 * @param actor
	 *            - le héros
	 */
	public static void setPlayer(MapActor actor) {
		player = actor;
	}

	/**
	 * Initialise la boite de texte
	 * 
	 * @param textBox
	 *            - la boite de texte à afficher
	 */
	public static void setTextBox(TextBox textBox) {
		GameViewPort.textBox = textBox;
	}

	/**
	 * Lance le traitement de mise à jour global
	 * 
	 * @param mouseX
	 *            - la position en X du curseur
	 * @param mouseY
	 *            - la position en y du curseur
	 * @param inputs
	 *            - une Map des entrées utilisateur
	 * @throws SlickException
	 */
	public static void update(Input input) throws SlickException {
		if (panel == null && textBox == null) {
			// Pas de panneau à gérer, on s'occupe du jeu
			handleGameLoop(input);
		} else {
			if (panel != null) {
				// Il y a un panneau d'affiché, il faut le gérer
				panel.update(input);
			}
			if (textBox != null) {
				textBox.update(input);
				if (textBox.isToClose()) {
					textBox = null;
				}
			}
		}
	}
}
